/////////////////////////////////////////////////////////////////////
//
//  MOE Framework (Meta Object Encapsulation Framework)
//
//  Original Author: Wiksy Wang, 2011
//
/////////////////////////////////////////////////////////////////////

#ifndef MOE_PERSIST_NAMESPACE_PROPERTY_TREE_READER_HPP
#define MOE_PERSIST_NAMESPACE_PROPERTY_TREE_READER_HPP

#include "DefaultNamespaceTreeFormat.hpp"
#include "PropertyTreeReader.hpp"
#include "../ObjectTypeManager.h"
#include "../Name/NamedObjectCacheManager.h"
#include "../Name/Namespace.h"

namespace MOE
{

class ClassMeta;
class DataMemberMeta;

namespace Persist
{

template <class TreeType, template <class> class TreeFormatPolicy = DefaultNamespaceTreeFormat>
class NamespacePropertyTreeReader
{
public:
	NamespacePropertyTreeReader()
	{
	}

	bool Read(const TreeType& ptree) const
	{
		Name::Namespace* namespacePtr = &Name::Namespace::GetGlobalNamespace();

		// Find the namespace entry. We assume there is only one such entry.
		TreeType::const_assoc_iterator namespaceEntry = ptree.find(TreeFormatPolicy<TreeType>::NamespaceField());
		if (namespaceEntry != ptree.not_found())
		{
			namespacePtr = namespacePtr->CreateChildNamespace(Name::NamePath(namespaceEntry->second.get_value<TreeType::data_type>()));
		}

		// If we can't get into the right namespace, then we fail.
		if (namespacePtr == NULL)
		{
			return false;
		}

		// Find the object list and iterate through. We assume there is only one such entry, again.
		TreeType::const_assoc_iterator listEntry = ptree.find(TreeFormatPolicy<TreeType>::ObjectListField());

		// But this time, we succeed if we can't find it, because we have "read all entries".
		if (listEntry == ptree.not_found())
		{
			return true;
		}

		bool success = true;

		PropertyTreeReader<TreeType, TreeFormatPolicy> objReader(namespacePtr);
		const TreeType& listTree = listEntry->second;
		for (TreeType::const_iterator it = listTree.begin(), itEnd = listTree.end(); it != itEnd; ++it)
		{
			if (it->first == TreeFormatPolicy<TreeType>::ObjectField())
			{
				success &= ReadObject(it->second, namespacePtr, objReader);
			}
		}

		return success;
	}

	bool ReadObject(const TreeType& ptree, Name::Namespace* namespacePtr, PropertyTreeReader<TreeType, TreeFormatPolicy>& objReader) const
	{
		TreeType::const_assoc_iterator valueEntry = ptree.find(TreeFormatPolicy<TreeType>::ObjectValueField());
		if (valueEntry == ptree.not_found())
		{
			return false;
		}

		TreeType::data_type objType = ptree.get<TreeType::data_type>(TreeFormatPolicy<TreeType>::ObjectTypeField(), TreeType::data_type());
		if (objType.empty())
		{
			return false;
		}

		TreeType::data_type objName = ptree.get<TreeType::data_type>(TreeFormatPolicy<TreeType>::ObjectNameField(), TreeType::data_type());
		if (objName.empty())
		{
			return false;
		}

		ClassMetaProxy metaProxy = ObjectTypeManager::GetSingleton().GetClassMeta(objType);
		if (!metaProxy.IsValid())
		{
			return false;
		}
		const ClassMeta& meta = metaProxy;
		Address newObject = meta.New();

		if (objReader.Read(meta, valueEntry->second, newObject))
		{
			namespacePtr->SetObject(Name::NamePath(objName), newObject, meta.GetClassID());

			// Call cached handlers to set any pointers pointing to this object.
			Name::NamedObjectCacheManager::GetSingleton().Handle(namespacePtr->GetChildFullPath(Name::NamePath(objName)), newObject);

			return true;
		}
		else
		{
			meta.Delete(newObject);
			return false;
		}
	}
};

}

}

#endif
